package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.ByteUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisKeyConstant;
import com.tianji.learning.domain.dto.PointsRecordAddDto;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.service.SignRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import static com.tianji.common.constants.MqConstants.Exchange.LEARNING_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.SIGN_IN;

@Service
@Slf4j
public class SignRecordServiceImpl implements SignRecordService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitMqHelper mqHelper;

    /**
     * 添加签到记录,到redis中以 bitMap的形式
     *
     * @return 连续签到天数和今日签到获取的积分
     */
    @Override
    public SignResultVO saveSignRecord() {
        //1、组装redisKey并获取userId
        //1.1、获取userId,和当前年月
        Long user = UserContext.getUser();
        String date = LocalDateTime.now().format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        int dayOfMonth = LocalDateTime.now().getDayOfMonth();

        //1.2、组装redisKey
        String key = RedisKeyConstant.SIGN_RECORD_KEY + user + date;

        //2、存入redis中
        Boolean exists = redisTemplate.opsForValue().setBit(key, dayOfMonth-1, true);
        if (BooleanUtils.isTrue(exists)) {
            throw new BizIllegalException("今天已经签过到了哦~~");
        }

        //2.1、设置过期时间
        Duration between = DateUtils.between(LocalDateTime.now(),DateUtils.getMonthBeginTime(LocalDate.now().plusMonths(1)));
        redisTemplate.expire(key, Duration.ofSeconds(between.getSeconds()));

        //3、计算连续签到天数并计算奖励分数
        int days = getSignDays(key, dayOfMonth);
        int rewardPoints = getRewardPoints(days);

        //4、封装返回结果
        SignResultVO signResultVO = new SignResultVO();
        signResultVO.setSignDays(days);
        signResultVO.setRewardPoints(rewardPoints);

        //5、发送消息添加积分记录
        PointsRecordAddDto pointsRecordAddDto = new PointsRecordAddDto();
        pointsRecordAddDto.setPoints(signResultVO.totalPoints());
        pointsRecordAddDto.setUserId(user);

        try {
            mqHelper.send(LEARNING_EXCHANGE,SIGN_IN,pointsRecordAddDto);
        } catch (Exception e) {
            log.error("发送消息失败",e);
        }
        return signResultVO;
    }

    /**
     * 查询签到记录
     *
     * @return 签到记录的byte数组
     */
    @Override
    public Byte[] getSignRecord() {
        //1、查询第一天到今天的签到记录
        //1.1、获取userId,和当前年月
        Long user = UserContext.getUser();
        String date = LocalDateTime.now().format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        int dayOfMonth = LocalDateTime.now().getDayOfMonth();

        //1.2、组装redisKey
        String key = RedisKeyConstant.SIGN_RECORD_KEY + user + date;

        //2、查询第一天到今天的签到记录
        List<Long> list = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));


        if (ObjectUtil.isEmpty(list) || ObjectUtil.isEmpty(list.get(0))||list.get(0) == 0) {
            return new Byte[0];
        }
        int record = list.get(0).intValue();

        //3、封装返回结果的Byte数组
        Byte[] bytes = new Byte[dayOfMonth];
        int pos = dayOfMonth-1;
        while (pos>=0){
            //3.1、与1作与运算，相当于只取最后一位赋值给Byte数组相应位置
            bytes[pos] = ByteUtils.intToByte(record & 1 );
            //3.2、将record 右移一位相当于去掉最后一位
            record >>>= 1;
            pos--;
        }

        return bytes;
    }

    //计算连续签到天数
    private int getSignDays(String key, int dayOfMonth) {
        //1、查询第一天到今天的签到记录
        List<Long> list = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        //2、计算连续签到天数
        int days = 0;
        if (ObjectUtil.isEmpty(list) || ObjectUtil.isEmpty(list.get(0))) {
            return days;
        }
        int record = list.get(0).intValue();
        //3、与1作与运算，相当于只取最后一位
        while ((record & 1) == 1) {
            days++;
            //3.1、将record 右移一位相当于去掉最后一位
            record >>>= 1;
        }

        return days;
    }

    //计算连续签到奖励分数
    private int getRewardPoints(int days) {
        switch (days){
            case 7:
                return 10;
            case 14:
                return 20;
            case 28:
                return 40;
            default:
                return 0;
        }
    }
}
